//
//  ResponderChainAdvancedViewController.m
//  OCUIBootcamp
//
//  Created by 周晓旺 on 2023/11/29.
//

#import "ResponderChainAdvancedViewController.h"
#import <Masonry.h>
#import <UIKit/UIKit.h>
#import "RCView.h"
#import "RCButton.h"
@interface ResponderChainAdvancedViewController ()
@property (nonatomic, strong) RCView *bgView;
@end

@implementation ResponderChainAdvancedViewController
//ResponderChain 响应者链的应用
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = UIColor.lightGrayColor;
    [self responderChain1];
//    MARK: - 0.响应者链的方法1 和 方法2

    /* 同一个视图，先走方法1 再走方法2，设置当前视图点击范围可以只用方法2，重写方法1也要注意范围别写错了
     方法1:- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
     方法2：- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
     方法2注释// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
     //递归调用-pointInside:withEvent:。点在接收者的坐标系中

     ⚠️调试1发现的知识点：
     bounds是相对于自身来说的，frame是相对于父视图来说的(并不是相对于window来说的)
     pointInside方法对于当前视图本身 是相对于bounds来说的，看点击的point是否在自己的bounds里，
     若使用self.frame 会导致范围错误，可能返回不在自己范围内，这样设置的话 [super hitTest:point withEvent:event]也会返回nil

     
     ✅调试2：
     然后我修改了父视图的方法1，这样可以成功，这样其实是只修改的父视图的响应者链，详见：‼️3.优先级
     ⚠️响应者链修改子视图的响应时请注意：
     若修改父视图的响应者链，则需要子视图的frame(point是相对于父视图来说的)
     若修改子视图的响应者链，则需要子视图的bounds(point是相对于子视图来说的)
     
     ‼️3.优先级
     我发现设置父视图的方法1，则点击后并未调用子视图的方法1和方法2
     因为父视图方法1是递归调用方法2，找点击在了当前视图的哪个子View上，
     已经开始找子View了，说明点击范围已经确认在当前视图上了，所以当然不会再在本层调用方法2了（应该是在当前视图的parentView上已经确认过了）
     */
    [self responderChain2];
    
}
//MARK: - 1.  子视图范围超过父视图，点击子视图无响应的解决方案
/*
 子视图范围超过父视图，点击子视图无响应的解决方案
 */
- (void)responderChain1 {
    //背景添加一个基础点击响应
    UIButton *bgBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    bgBtn.backgroundColor = UIColor.whiteColor;
    [bgBtn addTarget:self action:@selector(bgBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:bgBtn];
    [bgBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
    
    //黄色背景bgView
    RCView *bgView = [[RCView alloc] init];
    self.bgView = bgView;
    bgView.backgroundColor = UIColor.yellowColor;
    [self.view addSubview:bgView];
    [bgView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).offset(80);
        make.left.right.equalTo(self.view);
        make.height.equalTo(self.view.mas_height).multipliedBy(0.3);
    }];
    
    //超出bgView的红色按钮，设置父视图pointerInside范围可实现超出父视图仍可响应点击事件
    UIButton *topBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    topBtn.backgroundColor = UIColor.systemPinkColor;
    [topBtn addTarget:self action:@selector(topBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
    [bgView addSubview:topBtn];
    [topBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(bgView).offset(100);
        make.left.equalTo(bgView).offset(50);
        make.right.equalTo(bgView).offset(-250);
        make.height.equalTo(bgView).multipliedBy(2);
    }];

//MARK: - autolayout布局获取frame的问题
    /*
     ⚠️问题1:
     autolayout设置完布局后，布局引擎并不会立即更新，而是将布局置为待更新状态。
     若需获取frame，需使用layoutIfNeeded强制刷新布局
     ⚠️问题2
     [topBtn layoutIfNeeded];
     topBtn自身强制刷新布局，只能确保自身的宽高正确，x、y仍是错误的。需要self.view整体设置布局layoutIfNeeded
     topBtn.fram = {{-146.5, -255.66666666666666}, {293, 511.33333333333331}}
     */
// Allows you to perform layout before the drawing cycle happens. -layoutIfNeeded forces layout early
//    [topBtn layoutIfNeeded];
    [self.view layoutIfNeeded];
    NSLog(@"topBtn.fram = %@",NSStringFromCGRect(topBtn.frame));
    bgView.extraFrames = topBtn.frame;
    
}
- (void)bgBtnClicked: (UIButton *)btn{
    NSLog(@"底层Btn基础点击事件");
}
- (void)topBtnClicked: (UIButton *)btn {
    NSLog(@"顶层红Btn点击事件");
}
// MARK: - 2.扩大Button的响应范围
- (void)responderChain2 {
    //增加响应范围的黑色按钮，上下左右各加20的范围，都是黑色按钮的响应范围，
    //一种实现方式：可以在父视图的hitTest里设置黑色按钮范围和扩大范围的点，都返回biggerBtn，可实现增加黑色按钮响应范围的功能
    RCButton *biggerBtn = [RCButton buttonWithType:UIButtonTypeCustom];
    biggerBtn.tag = 100;
    biggerBtn.backgroundColor = UIColor.blackColor;
    [biggerBtn addTarget:self action:@selector(biggerBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.bgView addSubview:biggerBtn];
//    biggerBtn.frame = CGRectMake(250, 80, 100, 100);
    [biggerBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.bgView.mas_right).offset(-40);
        make.top.equalTo(self.bgView).offset(80);
        make.bottom.equalTo(self.bgView).offset(-20);
        make.width.equalTo(@80);
    }];
    [self.view layoutIfNeeded];

//  MARK: - 3. 顶层视图与 命中目标视图 的解释（此处顶层视图指UI最顶层，也是从UIWindow开始算起 树的最底层的）
    /*无需也无法再调用biggerBtn响应者链方法的原因
     方法1和方法2是寻找子view是否能响应的方法
     方法1:- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
     方法2:- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
     当前情况下：self.bgView通过方法1找到了 “目标视图”（点击位置的最上层的视图）
     所以btn就不会在调用方法1、或方法2了。
     最上层视图(可能非“目标视图”)不会再调用方法1，但仍可能会调用方法2确认点击范围是否在自己身上。
     */
    //扩大btn范围的方法
//    biggerBtn.touchInsets = UIEdgeInsetsMake(-20, -20, -20, -20);
    
    //扩大btn点击范围的另一种方法：通过点击point的位置，在父视图的hitTest方法里直接返回biggerBtn，也可实现扩大biggerBtn点击范围的效果
    UIEdgeInsets touchInsets = UIEdgeInsetsMake(-20, -20, -20, -20);
    CGRect expandedFrame = UIEdgeInsetsInsetRect(biggerBtn.frame, touchInsets);// CGRectInset(biggerBtn.frame, 20, 20);
    self.bgView.expandedFrame = expandedFrame;

}
- (void)biggerBtnClicked:(UIButton *)btn {
    NSLog(@"点击范围扩大的黑色Btn的点击事件");
}
/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end
